#ifndef BACKPACK_HOTSPOT_MANAGER_H_
#define BACKPACK_HOTSPOT_MANAGER_H_

#include "HotSpot.hpp"
#include "Action.hpp"
#include "Item.hpp"

#include <list>
#include <memory>

#include <boost/variant/static_visitor.hpp>

namespace backpack {




template <class DerivedBuilder>
class HotSpotBuilder {
public:
  HotSpotBuilder(std::list<HotSpotVariant> &storage, std::string const& name)
    : m_rListStorage(storage), m_strName(name), m_hotSpotChild(g_hotSpotNull),
    m_bEnabled(false), m_bVisible(true) {
  }
  DerivedBuilder& withDescription(std::string const& description) {
    m_strDescription = description;
    return *derivedCast();
  }
  DerivedBuilder& withChoice(std::string const& choice) {
    m_strChoice = choice;
    return *derivedCast();
  }
  DerivedBuilder& withLocation(std::string const& location) {
    m_strLocation = location;
    return *derivedCast();
  }
  DerivedBuilder& withLockedMessage(std::string const& lockedString) {
    m_strLocked = lockedString;
    return *derivedCast();
  }
  DerivedBuilder& withChildHotSpot(HotSpotVariant const& hotSpot) {
    m_hotSpotChild = hotSpot;
    return *derivedCast();
  }
  DerivedBuilder& enabled() {
    m_bEnabled = true;
    return *derivedCast();
  }
  DerivedBuilder& disabled() {
    m_bEnabled = false;
    return *derivedCast();
  }
  DerivedBuilder& visible() {
    m_bVisible = true;
    return *derivedCast();
  }
  DerivedBuilder& invisible() {
    m_bVisible = false;
    return *derivedCast();
  }

  HotSpotVariant build() {
    HotSpotVariant variant = derivedCast()->buildImpl();
    m_rListStorage.push_back(variant);
    return variant;
  }

  operator HotSpotVariant() {
    return build();
  }

protected:
  std::list<HotSpotVariant> &m_rListStorage;
  std::string m_strName;
  std::string m_strDescription;
  std::string m_strChoice;
  std::string m_strLocation;
  std::string m_strLocked;
  HotSpotVariant m_hotSpotChild;
  bool m_bEnabled;
  bool m_bVisible;

private:
  DerivedBuilder const* derivedCast() const {
    return static_cast<DerivedBuilder const*>(this);
  }
  DerivedBuilder* derivedCast() {
    return static_cast<DerivedBuilder*>(this);
  }
}; // class HotSpotBuilder



class UnlockPointBuilder : public HotSpotBuilder<UnlockPointBuilder> {
public:
  UnlockPointBuilder(
    std::list<HotSpotVariant> &storage,
    std::string const& name)
    : HotSpotBuilder(storage, name),
    m_action(g_actionNull),
    m_refKey(Key::c_null) {
  }
  template <class Action>
  UnlockPointBuilder& withAction(Action const& action) {
    assert (g_actionNull == m_action);
    m_action = action;
    return *this;
  }
  UnlockPointBuilder& unlockedWithKey(Key &key) {
    assert (Key::c_null == m_refKey.get());
    m_refKey = std::cref(key);
    return *this;
  }

  HotSpotVariant buildImpl() const;

private:
  std::reference_wrapper<const Key> m_refKey;
  ActionVariant m_action;
}; // class UnlockPointBuilder



class WayPointBuilder : public HotSpotBuilder<WayPointBuilder> {
public:
  WayPointBuilder(std::list<HotSpotVariant> &storage, std::string const& name)
    : HotSpotBuilder(storage, name), m_refNodeTarget(GameNode::c_null),
    m_ptrUnlockPointBuilder(nullptr) {
  }

  WayPointBuilder& withTargetNode(GameNode &target) {
    assert (GameNode::c_null == m_refNodeTarget.get());
    m_refNodeTarget = target;
    return *this;
  }
  WayPointBuilder& unlockedWith(UnlockPointBuilder &&builder) {
    assert (nullptr == m_ptrUnlockPointBuilder);
    m_ptrUnlockPointBuilder.reset(new UnlockPointBuilder(std::move(builder)));
    return *this;
  }
  WayPointBuilder& unlockedWith(UnlockPointBuilder &builder) {
    assert (nullptr == m_ptrUnlockPointBuilder);
    m_ptrUnlockPointBuilder.reset(new UnlockPointBuilder(builder));
    return *this;
  }
  WayPointBuilder& unlocked() {
    return enabled();
  }

  HotSpotVariant buildImpl() const;

private:
  std::reference_wrapper<const GameNode> m_refNodeTarget;
  std::shared_ptr<UnlockPointBuilder> m_ptrUnlockPointBuilder;
}; // class WayPointBuilder



class ItemPointBuilder : public HotSpotBuilder<ItemPointBuilder> {
public:
  ItemPointBuilder(std::list<HotSpotVariant> &storage, std::string const& name)
    : HotSpotBuilder(storage, name), m_ptrUnlockPointBuilder(nullptr) {
  }

  template <class Variant>
  ItemPointBuilder& withItem(Item<Variant> &item) {
    return withItem(std::cref(static_cast<Variant &>(item)));
  }
  ItemPointBuilder& withItem(ItemVariant const& item) {
    m_listItems.push_back(item);
    return *this;
  }
  ItemPointBuilder& unlockedWith(UnlockPointBuilder &&builder) {
    assert (nullptr == m_ptrUnlockPointBuilder);
    m_ptrUnlockPointBuilder.reset(new UnlockPointBuilder(std::move(builder)));
    return *this;
  }
  ItemPointBuilder& unlockedWith(UnlockPointBuilder &builder) {
    assert (nullptr == m_ptrUnlockPointBuilder);
    m_ptrUnlockPointBuilder.reset(new UnlockPointBuilder(builder));
    return *this;
  }
  ItemPointBuilder& unlocked() {
    return enabled();
  }

  HotSpotVariant buildImpl() const;

private:
  std::list<ItemVariant> m_listItems;
  std::shared_ptr<UnlockPointBuilder> m_ptrUnlockPointBuilder;
}; // class ItemPointBuilder



class LeverPointBuilder : public HotSpotBuilder<LeverPointBuilder> {
public:
  LeverPointBuilder(std::list<HotSpotVariant> &storage, std::string const& name)
    : HotSpotBuilder(storage, name), m_bOneShot(false) {
  }

  LeverPointBuilder& switchesVisibilityOf(HotSpotVariant &hotspot,
      std::string const& message) {
    m_listActions.push_back(VisibleSwitchAction(message, "", hotspot));
    return *this;
  }
  LeverPointBuilder& shows(HotSpotVariant &hotspot, std::string const& message){
    m_listActions.push_back(ShowAction(message, "", hotspot));
    return *this;
  }
  LeverPointBuilder& showsMessage(std::string const& message) {
    m_listActions.push_back(MessageAction(message));
    return *this;
  }
  LeverPointBuilder& endsGame() {
    m_listActions.push_back(EndAction());
    return *this;
  }
  LeverPointBuilder& oneShot(){
    m_bOneShot = true;
    return *this;
  }

  HotSpotVariant buildImpl() const;

private:
  std::list<ActionVariant> m_listActions;
  bool m_bOneShot;
}; // class LeverPointBuilder



class HotSpotManager {
public:
  ~HotSpotManager();

  WayPointBuilder newDoor(std::string const& name);
  UnlockPointBuilder newKeyHole(Key &key);
  ItemPointBuilder newChest(std::string const& name);
  LeverPointBuilder newLever(std::string const& name);

private:
  std::list<HotSpotVariant> m_listStorage;

  struct HotSpotVariantDeleter : public boost::static_visitor<> {
    template <class Variant>
    void operator() (HotSpot<Variant> const& hotspot) const {
      delete &hotspot;
    }
  } m_deleter; // struct HotSpotVariantDeleter
}; // class HotSpotManager





} // namespace backpack

#endif // BACKPACK_HOTSPOT_MANAGER_H_

